export class T_snowID {
  // 每台机器(节点)的ID位数 10位最大可以有2^10=1024个节点
  private workerBits: bigint;

  // 表示每个集群下的每个节点，1毫秒内可生成的id序号的二进制位数 即每毫秒可生成 2^12-1=4096个唯一ID
  private numberBits: bigint;

  private workerID: bigint;

  // 这里求最大值使用了位运算，-1 的二进制表示为 1 的补码
  // -1 ^ (-1 << nodeBits) 这里是不是等于 1023 节点ID的最大值，用于防止溢出
  private workerMax: bigint;

  // 用来表示生成id序号的最大值
  private numberMax: bigint;

  // 时间戳向左的偏移量
  private timeShift: bigint;

  private workerShift: bigint;

  // 记录的时间戳
  private timestamp: bigint;

  // 记录同一时间timestamp 产生的ID数
  private number: bigint;

  // 41位字节作为时间戳数值的话 大约68年就会用完
  // 假如你2010年1月1日开始开发系统 如果不减去2010年1月1日的时间戳 那么白白浪费40年的时间戳啊！
  // 这个一旦定义且开始生成ID后千万不要改了 不然可能会生成相同的ID 1596666666666
  private seed: bigint;

  constructor() {
    this.workerBits = 10n;
    this.numberBits = 12n;
    this.workerMax = -1n ^ (-1n << this.workerBits);
    this.numberMax = -1n ^ (-1n << this.numberBits);
    this.timeShift = this.workerBits + this.numberBits;
    this.workerShift = this.numberBits;
    this.seed = -1n;
    this.workerID = -1n;
    this.timestamp = 0n;
    this.number = 0n;
  }

  get snowSeed(): bigint {
    return this.seed;
  }

  set snowSeed(seed: bigint) {
    this.seed = seed;
  }

  get snowWorkID(): bigint {
    return this.workerID;
  }

  set snowWorkID(workID: bigint) {
    this.workerID = workID;
  }

  public getSnowID(): bigint {
    // 要先检测workerId是否在上面定义的范围内
    if (
      this.workerID < 0 ||
      this.workerID > this.workerMax ||
      this.seed < 1596666666666n
    ) {
      return -1n;
    }

    // 获取生成时的时间戳
    let now: bigint = BigInt(Date.now()); // 毫秒
    if (this.timestamp === now) {
      this.number++;
      // 这里要判断，当前工作节点是否在1毫秒内已经生成numberMax个ID
      if (this.number > this.numberMax) {
        // 如果当前工作节点在1毫秒内生成的ID已经超过上限 需要等待1毫秒再继续生成
        while (now <= this.timestamp) {
          now = BigInt(Date.now());
        }
        this.number = 0n;
      }
    } else {
      // 如果当前时间与工作节点上一次生成ID的时间不一致 则需要重置工作节点生成ID的序号
      this.number = 0n;
      this.timestamp = now; // 将机器上一次生成ID的时间更新为当前时间
    }

    // 第一段 now - epoch 为该算法目前已经奔跑了xxx毫秒
    // 如果在程序跑了一段时间修改了epoch这个值 可能会导致生成相同的ID
    return BigInt(
      ((now - this.seed) << this.timeShift) |
        (this.workerID << this.workerShift) |
        this.number
    );
  }
}
export interface I_snowID extends T_snowID {}
